package com.demo.bmsconsole;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import com.demo.bmsconsole.State.EInit;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class ADBServer  
{
	private static String TAG;
	private final Context mCtx;
	private static int mPort;

	private static ServerSocket mServer; // mServer socket
	private static Socket mConnection; // mConnection to client
	private static Thread mThread = new Thread (new Runnable() {
			public void run() {
				runServer();
			}
	});
	private static Handler mHandler = new Handler () {
		public void handleMessage(Message msg) {
			sendData(msg);
		}
	};
	private static BufferedReader mBufferedReader;
	private static BufferedOutputStream mBufferedOutputStream;
	private static OutputStream mOutputStream;

	// Public Methods follow:
	public ADBServer(Context ctx, final int port)
	{
		mCtx = ctx;
		TAG = mCtx.getPackageName();
		mPort = port;
	}
	// set up and run mServer 

	public static void run()
	{
		mThread.start();
	}

	public static void stop()
	{
		closeConnection();
	}

	public static void runServer()
	{
		try {
			mServer = new ServerSocket(mPort);
			mServer.setSoTimeout(0);
			while ( true ) {
				try {
					waitForConnection(); 
					getStreams();
					processConnection();
				} 
				catch ( EOFException eofException )	{
					Log.i( TAG, "EOF from Arduino" );
				} 
				finally {
					closeConnection();
				}
			} // end while
		} // end try
		catch ( IOException e ) 
		{
			Log.d(TAG, e.toString());
       		e.printStackTrace();
       		// log("Exception in Socket Server: " + e.toString());
		} // end catch
	} // end method runServer

	// public method to communicate with the running ADB server communication thread.
	public static boolean send(String string)
	{
		if (mConnection == null || !mConnection.isConnected()) {
			return false;
		}
		mHandler.sendMessage(mHandler.obtainMessage(0,string));
		return true;
	}

	// Private Methods:
	private static void waitForConnection() throws IOException
	{
		Log.d(TAG, "waiting...");
		mConnection = mServer.accept();
		mConnection.setSoLinger(false, 0);
		mConnection.setTcpNoDelay(true);
		mOutputStream = mConnection.getOutputStream();
		Log.d(TAG, "got connection.");
	}

	// get streams to send and receive data
	private static void getStreams() throws IOException
	{
		// set up mOutput stream for objects
		mBufferedOutputStream = new BufferedOutputStream(mOutputStream);

		// set up mInput stream for objects
		mBufferedReader = new BufferedReader(new InputStreamReader(mConnection.getInputStream()));

		Log.d(TAG, "got streams");
	}

	// process mConnection with client
	private static void processConnection() throws IOException
	{
		String line;
		// Set state to indicate we're processing connection.
		State.setCommStatus(true);
		
		while ((line = mBufferedReader.readLine()) != null) {
			Interpreter.handle(line);
		}
		mBufferedReader.close();
		mBufferedOutputStream.close();
		Log.d(TAG,"done reading from client");
	} // end method processConnection

	// close streams and socket
	private static void closeConnection() 
	{
		Log.i(TAG, "Closing connection");
		try 
		{
			if (mBufferedOutputStream != null) {
				mBufferedOutputStream.close(); // close mOutput stream
			}
			if (mBufferedReader != null) {
				mBufferedReader.close(); // close mInput stream
			}
			if (mConnection != null) {
				mConnection.close(); // close socket
			}
		} // end try
		catch ( IOException ioException ) 
		{
			ioException.printStackTrace();
		} // end catch
	} // end method closeConnection


	// send message to client, called by mHandler
	private static void sendData( Message msg )
	{
		String data = new String();
		if (msg.obj.getClass() != String.class) {
			Log.e(TAG,"Bad Message object");
			return;
		}
		data = (String)msg.obj;
		sendStringData(data+"\n");
	} // end method sendData

	private static void sendStringData(String data) {
		try // send object to client
		{
			String ls = new String("writing:" + data);
			if (mBufferedOutputStream != null) {
				Log.d(TAG, ls);
				LogFile.log(ls);
				mBufferedOutputStream.write(data.getBytes());
				mBufferedOutputStream.flush(); // flush mOutput to client
			} else {
				LogFile.log("No Client connection! " + ls);
			}
			return;
		} // end try
		catch ( IOException e ) 
		{
			Log.i(TAG, "exc sending " + e.toString());
			// logToConsole();
			return;
		} // end catch
	}

} // end class Server
